#!/usr/bin/env python3
"""
Security Validator - Enterprise Security Framework
基于enterprise_security_framework.yaml的零信任安全验证

核心原则：
- 零信任架构：默认拒绝所有访问
- 多层防护：纵深防御策略
- 最小权限原则：仅授予必需权限
"""

import os
import re
import yaml
from pathlib import Path
from typing import Dict, Any, List, Optional
import logging

logger = logging.getLogger(__name__)

class SecurityValidator:
    """企业级安全验证器 - 零信任架构实现"""
    
    def __init__(self):
        self.project_root = Path(__file__).parent.parent.parent
        self.allowed_file_patterns = [
            r".*\.yaml$",
            r".*\.yml$", 
            r".*\.py$",
            r".*\.md$",
            r".*\.txt$"
        ]
        self.blocked_patterns = [
            r".*\.\./.*",  # 路径遍历
            r".*<script.*",  # XSS
            r".*exec\(.*",  # 代码执行
            r".*eval\(.*",  # 代码执行
            r".*import os.*system.*",  # 危险系统调用
        ]
        
    def validate_input(self, data: str) -> bool:
        """
        验证所有输入数据 - 零信任原则
        默认拒绝，只允许安全的内容通过
        """
        if not data or not isinstance(data, str):
            return False
            
        # 检查恶意模式
        for pattern in self.blocked_patterns:
            if re.search(pattern, data, re.IGNORECASE):
                logger.warning(f"Blocked malicious pattern: {pattern}")
                return False
        
        # 长度限制
        if len(data) > 100000:  # 100KB限制
            logger.warning("Input too large")
            return False
            
        return True
    
    def validate_file_path(self, file_path: str) -> bool:
        """
        验证文件路径安全性 - 最小权限原则
        只允许项目内的特定文件类型
        """
        if not self.validate_input(file_path):
            return False
            
        try:
            path = Path(file_path).resolve()
            
            # 确保在项目根目录内
            if not str(path).startswith(str(self.project_root.resolve())):
                logger.warning(f"Path outside project root: {path}")
                return False
            
            # 检查文件扩展名
            file_allowed = False
            for pattern in self.allowed_file_patterns:
                if re.match(pattern, str(path)):
                    file_allowed = True
                    break
                    
            if not file_allowed:
                logger.warning(f"File type not allowed: {path}")
                return False
                
            return True
            
        except Exception as e:
            logger.error(f"Path validation error: {e}")
            return False
    
    def check_file_permissions(self, file_path: str) -> bool:
        """
        检查文件权限 - 最小权限原则
        确保有适当的读写权限
        """
        if not self.validate_file_path(file_path):
            return False
            
        try:
            path = Path(file_path)
            
            # 检查文件是否存在且可读
            if path.exists():
                if not os.access(path, os.R_OK):
                    logger.warning(f"No read permission: {path}")
                    return False
            else:
                # 检查父目录是否可写（用于创建文件）
                parent = path.parent
                if not parent.exists() or not os.access(parent, os.W_OK):
                    logger.warning(f"No write permission to parent: {parent}")
                    return False
                    
            return True
            
        except Exception as e:
            logger.error(f"Permission check error: {e}")
            return False
    
    def sanitize_yaml_input(self, yaml_content: str) -> str:
        """
        清理YAML输入 - 多层防护
        移除潜在的危险内容
        """
        if not self.validate_input(yaml_content):
            raise SecurityError("Invalid YAML input")
        
        # 移除潜在危险的YAML标签
        dangerous_tags = [
            "!!python/object/apply:",
            "!!python/object/new:",
            "!!python/name:",
            "!!python/module:",
        ]
        
        sanitized = yaml_content
        for tag in dangerous_tags:
            sanitized = sanitized.replace(tag, "# REMOVED_DANGEROUS_TAG")
            
        return sanitized
    
    def validate_yaml_structure(self, yaml_content: str) -> bool:
        """
        验证YAML结构安全性 - 零信任验证
        确保YAML内容符合预期结构
        """
        try:
            sanitized_content = self.sanitize_yaml_input(yaml_content)
            data = yaml.safe_load(sanitized_content)
            
            if data is None:
                return True  # 空YAML文件是安全的
                
            # 检查是否为字典结构
            if not isinstance(data, dict):
                logger.warning("YAML root must be dictionary")
                return False
            
            # 检查嵌套深度（防止DoS）
            if self._check_nesting_depth(data, max_depth=10):
                logger.warning("YAML nesting too deep")
                return False
                
            return True
            
        except yaml.YAMLError as e:
            logger.error(f"YAML validation error: {e}")
            return False
        except Exception as e:
            logger.error(f"YAML structure validation error: {e}")
            return False
    
    def _check_nesting_depth(self, obj: Any, current_depth: int = 0, max_depth: int = 10) -> bool:
        """检查嵌套深度"""
        if current_depth > max_depth:
            return True
            
        if isinstance(obj, dict):
            for value in obj.values():
                if self._check_nesting_depth(value, current_depth + 1, max_depth):
                    return True
        elif isinstance(obj, list):
            for item in obj:
                if self._check_nesting_depth(item, current_depth + 1, max_depth):
                    return True
                    
        return False
    
    def secure_file_operation(self, file_path: str, operation: str = "read") -> bool:
        """
        安全文件操作检查 - 综合安全验证
        在执行任何文件操作前进行完整安全检查
        """
        # 1. 输入验证
        if not self.validate_input(file_path):
            raise SecurityError("Invalid file path input")
        
        # 2. 路径验证
        if not self.validate_file_path(file_path):
            raise SecurityError("Unsafe file path")
            
        # 3. 权限检查
        if not self.check_file_permissions(file_path):
            raise SecurityError("Insufficient file permissions")
        
        # 4. 操作类型验证
        allowed_operations = ["read", "write", "append"]
        if operation not in allowed_operations:
            raise SecurityError(f"Invalid operation: {operation}")
            
        logger.info(f"Secure file operation approved: {operation} {file_path}")
        return True


class SecurityError(Exception):
    """自定义安全异常"""
    pass


# 全局安全验证器实例
security_validator = SecurityValidator()

def validate_input(data: str) -> bool:
    """全局输入验证函数"""
    return security_validator.validate_input(data)

def secure_file_operation(file_path: str, operation: str = "read") -> bool:
    """全局安全文件操作函数"""
    return security_validator.secure_file_operation(file_path, operation)

def sanitize_yaml(yaml_content: str) -> str:
    """全局YAML清理函数"""
    return security_validator.sanitize_yaml_input(yaml_content) 